# ---------- 简介 ---------- #
# BaseSimulator.py
# 这个文件实现的是各个模拟器共有的功能; 通过将这里实现的类作为
# 父类,我们可以避免将相同的逻辑在每个模拟器中都实现一次
# 赵宗义, xinshengzzy@foxmail.com, 2021.01.12
# ---------- #
import mytools
import 文件工具
from simulators.事件管理器 import 事件管理器


class BaseSimulator(object):
    def __init__(self):
        # ++++++++++ 输入缓冲区 ++++++++++ #
        self.输入缓冲区 = 事件管理器()
        # ------------------- #
        # ---------- 在线测量相关变量 ---------- #
        self.在线测量 = False
        self.在线流长估计 = []
        # ---------- #
        self.数据包数 = 0
        self.导出次数 = 0
        self.升级次数 = 0  # 进行promotion的次数
        self.匹配次数 = 0
        # ---------- 周期性导出相关变量 ----------#
        self.导出周期 = -1
        self.导期起点 = -1  # 导出周期起始状态
        # ---------- #
        # ---------- 超时导出相关变量 ----------#
        self.超时时间 = -1
        # -------------------- #
        # ++++++++++ 模式切换 ++++++++++ #
        self.周期模式 = 1
        self.超时模式 = 2
        self.导出模式 = self.周期模式
        # -------------------- #
        # ++++++++++ 统计模式 ++++++++++ #
        self.包模式 = 1
        self.字节模式 = 2
        self.统计模式 = self.包模式
        # ---------- #
        # ---------- 协议类型 ---------- #
        self.协议 = "ipv4"
        # -------------------- #
        # ---------- 内存相关变量 ---------- #
        self.内存 = 0
        self.表长1 = 0
        self.表长2 = 0
        self.表长3 = 0
        self.表长4 = 0
        self.表长5 = 0
        self.表长6 = 0
        self.表长7 = 0
        self.表长8 = 0
        self.表长9 = 0
        # -------------------- #
        # ---------- 运行时间 ---------- #
        self.开始时间 = -1
        self.结束时间 = -1
        # -------------------- #
        # ++++++++++ 流记录集 ++++++++++ #
        self.流记录集 = dict()
        # ---------- #

    def 设置周期模式(self):
        self.导出模式 = self.周期模式

    def 设置超时模式(self):
        self.导出模式 = self.超时模式

    def 设置包模式(self):
        self.统计模式 = self.包模式

    def 设置字节模式(self):
        self.统计模式 = self.字节模式

    def 设置导出周期(self, 导出周期):
        self.导出周期 = 导出周期

    def 获取导出周期(self):
        return self.导出周期

    def 计算导出周期(self):
        pass

    def 设置超时时间(self, 超时时间):
        self.超时时间 = 超时时间

    def 获取超时时间(self):
        return self.超时时间

    def 在线流长查询(self, 标识符):
        pass

    def 记录在线流长(self, 标识符):
        if self.在线测量:
            流长 = self.在线流长查询(标识符) + 1  # 这里加1是因为我们再插入当前数据包之前进行查询
            self.在线流长估计.append(流长)

    def 备份在线流长估计(self, 文件名):
        文件工具.备份数据结构(self.在线流长估计, 文件名)

    def 获取在线流长估计(self):
        return self.在线流长估计

    def 下载数据平面(self):
        pass

    def 获取流记录集(self):
        流集 = self.合并流集(self.下载数据平面(), self.流记录集)
        return 流集

    def 更新流集(self, 流集):
        self.流记录集 = self.合并流集(self.流记录集, 流集)

    def 合并流集(self, 流集1, 流集2):
        res = 流集1.copy()
        for 标识符, 计数值 in 流集2.items():
            if 标识符 not in res:
                res[标识符] = 0
            res[标识符] += 计数值
        return res

    def 重置数据平面(self):
        pass

    def 导出数据平面(self):
        self.导出次数 += 1
        流集 = self.下载数据平面()
        self.更新流集(流集)
        self.重置数据平面()

    def 导出流记录(self, 流记录):
        self.导出次数 += 1
        标识符, 计数值 = 流记录
        if 标识符 not in self.流记录集:
            self.流记录集[标识符] = 计数值
        else:
            self.流记录集[标识符] += 计数值

    def 获取导出次数(self):
        return self.导出次数

    def 获取数据包数(self):
        return self.数据包数

    def 周期式数据包处理(self, 标识符, 时间戳):
        pass

    def 超时式数据包处理(self, 标识符, 时间戳):
        pass

    def 处理事件(self, 临量):
        if 临量.包类型():
            数据包 = 临量.提取数据包()
            if self.导出模式 == self.周期模式:
                if self.导出周期 > 0 and 数据包.ts - self.导期起点 > self.导出周期:
                    self.导出数据平面()
                    self.导期起点 = 数据包.ts
                self.周期式数据包处理(数据包)
            elif self.导出模式 == self.超时模式:
                self.超时式数据包处理(数据包)
            else:
                raise SystemExit("不能识别导出模式.")
        elif 临量.事务类型():
            print("事务类型")
        else:
            临量.print()
            raise SystemExit("不能识别事件类型.")

    def 计算指纹(self, 标识符):
        return mytools.hash5(标识符.encode('utf-8')) & 0xffffffff

    def 计算摘要(self, 标识符):
        return mytools.hash6(标识符.encode('utf-8')) & 0xffff

    def 计算索引(self, 标识符):
        pass

    def 配置内存方案(self):
        pass

    def 计算表长(self):
        pass

    def 获取匹配次数(self):
        return self.匹配次数

    def 添加包序列(self, 数据包序列):
        for 数据包 in 数据包序列:
            self.输入缓冲区.添加数据包(数据包)
        self.导期起点 = 数据包序列[0].ts
        self.开始时间 = 数据包序列[0].ts
        self.结束时间 = 数据包序列[-1].ts
        self.数据包数 = len(数据包序列)

    def 重放(self):
        while not self.输入缓冲区.为空():
            临量 = self.输入缓冲区.提取事件()
            if 临量.包类型():
                self.记录在线流长(临量.提取数据包().提取流标识符())
            self.处理事件(临量)
